package com.simulation;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;

import com.database.bean.Agent;
import com.database.bean.AgentTypeCompanyProduct;
import com.database.bean.CompanyProduct;
import com.database.bean.CompanyRoundDecisionAndResult;
import com.database.bean.Game;
import com.database.bean.Setting;
import com.logic.service.AdminService;
import com.math.util.CalculationUtil;

public class SimulationStrategyMain{
	
	public synchronized Game performStrategy(AgentsSimulation simState,Agent agent){

		Game game=simState.getGame();
		AdminService adminService=simState.getAdminService();
		Setting setting=adminService.getSetting();
		
		//System.out.println("Simulate Round: "+simulationRound);
		//System.out.println("Simulate Game: "+game);
		
		//Print out what agent is running
		System.out.println("Run "+agent.getAgentType().getAgentName());
		List<CompanyProduct> allOfferedProducts=simState.getAllOfferedProducts();
		System.out.println("Offered Products Amount: "+allOfferedProducts.size());
		
		if(allOfferedProducts.size()>0){
			
			TreeMap<Double,List<CompanyProduct>> utilityMap=new TreeMap <Double,List<CompanyProduct>>();
			Double minimumCompanyImage=simState.getMinimumCompanyImage();
			Double minimumProductQuality=simState.getMinimumProductQuality();
			Double maximumProductQuality=simState.getMaximumProductQuality();
			Double maximumCompanyImage=simState.getMaximumCompanyImage();
			Double minimumSellingPrice=simState.getMinimumSellingPrice();
			Double maximumSellingPrice=simState.getMaximumSellingPrice();
			
			//Evaluation of all offered Product with the Utiliy Function
			for(CompanyProduct companyProduct : allOfferedProducts){
				Double qualityFactor = companyProduct.getProduct().getQualityFactor();
				Double companyImageFactor = companyProduct.getCompanyRoundDecisionAndResult().getUser().getImageValue();
				
				Double referenceValuePrice=1.0;
				if((maximumSellingPrice-minimumSellingPrice)>0)
					referenceValuePrice = 1-((companyProduct.getSellingPrice()-minimumSellingPrice)/(maximumSellingPrice-minimumSellingPrice)); //im Vergleich zum niedrigsten angebotenen Preis
				
				Double referenceQualityFactor=1.0;
				if((maximumProductQuality-minimumProductQuality)>0)
					referenceQualityFactor=(qualityFactor-minimumProductQuality)/(maximumProductQuality-minimumProductQuality);
				
				Double referenceImageFactor=1.0;
				if((maximumCompanyImage-minimumCompanyImage)>0)
					referenceImageFactor=(companyImageFactor-minimumCompanyImage)/(maximumCompanyImage-minimumCompanyImage);
				
				Double agentPreferencePrice=agent.getPreferencePrice();
				Double agentPreferenceQuality=agent.getPreferenceQuality();
				Double agentPreferenceImage=agent.getPreferenceImage();
				
				Double utilityValue=CalculationUtil.calculateValue(agentPreferencePrice, agentPreferenceQuality, agentPreferenceImage, referenceValuePrice, referenceQualityFactor, referenceImageFactor,setting);
				
//				if(utilityValue>1){
//					System.out.println("agentPreferencePrice:"+agentPreferencePrice);
//					System.out.println("agentPreferenceQuality:"+agentPreferenceQuality);
//					System.out.println("agentPreferenceImage:"+agentPreferenceImage);
//					System.out.println("maximumCompanyImage:"+maximumCompanyImage);
//					System.out.println("minimumCompanyImage:"+minimumCompanyImage);
//					System.out.println("maximumProductQuality:"+maximumProductQuality);
//					System.out.println("minimumProductQuality:"+minimumProductQuality);
//					System.out.println("maximumSellingPrice:"+maximumSellingPrice);
//					System.out.println("minimumSellingPrice:"+minimumSellingPrice);
//					System.out.println("referenceValuePrice:"+referenceValuePrice);
//					System.out.println("referenceQualityFactor:"+referenceQualityFactor);
//					System.out.println("referenceImageFactor:"+referenceImageFactor);
//					System.out.println("utilityValue:"+utilityValue);
//					System.out.println("--------------------->0-----------------------------");
//				}
				
				//list of objects with same utilityValue in the Map
				if(utilityMap.containsKey(utilityValue)){
					utilityMap.get(utilityValue).add(companyProduct);
				}
				else{
					List<CompanyProduct> list=new ArrayList<CompanyProduct>();
					list.add(companyProduct);
					utilityMap.put(utilityValue, list);
				}

				//System.out.println("Utiliy for CompanyProduct "+companyProduct.getId()+" is "+utilityValue);
			}
			
			//Get highest element from the Utiliy TreeMap
			if(utilityMap.size() > 0){
				//Treemap last Elementhas highest Utiliy Value - Agent buys this!
				Double highestUtilityValue=utilityMap.lastKey();
				
				//just buy this product if has a least a defined utility for the consumer
				if(highestUtilityValue>=setting.getMinimumConsumerUtility()){
					List<CompanyProduct> list=utilityMap.get(highestUtilityValue);
					
					CompanyProduct companyProduct=null;
					
					//if there is just one companyProduct with this value
					if(list.size()==1){
						companyProduct=list.get(0);
					}
					else{
						int maxElement=list.size()-1;
						int randomIndex=randInt(0,maxElement);
						companyProduct=list.get(randomIndex);
					}
					
					agent.setCompanyProduct(companyProduct); //Agent buys this product
					adminService.update(agent);  //save
					
					//Revenue and Expenses for CompanyRoundDecisionAndResult is increased
					CompanyRoundDecisionAndResult crdar=companyProduct.getCompanyRoundDecisionAndResult();
					crdar.addRevenue(companyProduct.getSellingPrice());
					crdar.addExpenses(companyProduct.getProduct().getProductCosts());
					adminService.update(crdar);
					
					//amountSold increased
					companyProduct.increaseAmountSold();
					adminService.update(companyProduct);
									
					//AgentTypeCompanyProduct what is bought
					//if AgentTypeCompanyProduct combination is not available
					boolean found=false;
					List<AgentTypeCompanyProduct> atcps=agent.getAgentType().getAgentTypeCompanyProducts();
					for(AgentTypeCompanyProduct atcp : atcps){
						found=false;
						if(atcp.getCompanyProduct().getId()==companyProduct.getId()){
							atcp.increaseAmountSold(); //amount wurde erhöht
							adminService.update(atcp);
							found=true;
							break;
						}				
					}
					if(!found){  //if this combination is not there create it and save it
						AgentTypeCompanyProduct atcp=new AgentTypeCompanyProduct();
						atcp.setAmountSold(1);
						atcp.setAgentType(agent.getAgentType());
						atcp.setCompanyProduct(companyProduct);
						adminService.update(atcp); 
						agent.getAgentType().addAgentTypeCompanyProduct(atcp);
					}
					//System.out.println("Highest Utility: "+highestUtilityValue +" for CompanyProduct "+companyProduct.getId());
					//System.out.println("--------------------------------");
				}
				else{
					System.out.println("Utility of Product was to low "+highestUtilityValue+" smaller than "+setting.getMinimumConsumerUtility());
				}
			}
			else{
				System.out.println("No Products available in Utiliy Map!!");
			}
		}
		else{
			System.out.println("No Products available!!");
		}

		return game;
	}
	
	/**
	 * Returns a pseudo-random number between min and max, inclusive.
	 */
	private int randInt(int min, int max) {

	    // NOTE: Usually this should be a field rather than a method
	    // variable so that it is not re-seeded every call.
	    Random rand = new Random();

	    // nextInt is normally exclusive of the top value,
	    // so add 1 to make it inclusive
	    int randomNum = rand.nextInt((max - min) + 1) + min;

	    return randomNum;
	}
}
